﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Risk.Model
{
    public class Game : UniqueId
    {
        public List<Region> UnassignedRegions { get; set; }
        List<Continent> Continents { get; set; } 
        private readonly Continent _asia;
        private readonly Continent _northAmerica;
        private readonly Continent _europe;
        private readonly Continent _africa;
        private readonly Continent _australia;
        private readonly Continent _southAmerica;

        public List<Player> Players { get; set; }
        public List<Army> Armies { get; set; } 
        public Game()
        {
            _asia = new Continent(7);
            _northAmerica = new Continent(5);
            _europe = new Continent(5);
            _africa = new Continent(3);
            _australia = new Continent(2);
            _southAmerica = new Continent(2);
            Continents = new List<Continent> { _asia, _northAmerica, _europe, _africa, _australia, _southAmerica };

 

            Players = new List<Player>();
            InitializeUnassignedRegions();
            Armies = new List<Army>();
        }
        public Game(List<Player> players) : this()
        {
            Players = players;
        }


        public void InitializeGame()
        {
            InitializeArmies();
            InitializeArmyReserve();
            AssignRegions();
        }
        public void InitializeUnassignedRegions()
        {
            UnassignedRegions = new List<Region>
                {
                    new Region("afghanistan", _asia, "ukraine", "ural", "china", "india", "middle_east"),
                    new Region("alaska", _northAmerica, "alberta", "northwest_territory", "kamchatka"),
                    new Region("alberta", _northAmerica, "alaska", "northwest_territory", "ontario",
                               "western_united_states"),
                    new Region("argentina", _southAmerica, "peru", "brasil"),
                    new Region("brasil", _southAmerica, "peru", "venezuela", "argentina", "north_africa"),
                    new Region("central_america", _northAmerica, "eastern_united_states", "western_united_states",
                               "venezuela"),
                    new Region("china", _asia, "mongolia", "siberia", "ural", "afghanistan", "india", "siam"),
                    new Region("congo", _africa, "north_africa", "south_africa", "east_africa"),
                    new Region("eastern_australia", _australia, "western_australia", "new_guinea"),
                    new Region("eastern_united_states", _northAmerica, "quebec", "ontario", "western_united_states",
                               "central_america"),
                    new Region("east_africa", _africa, "middle_east", "egypt", "north_africa", "congo", "south_africa",
                               "madagascar"),
                    new Region("egypt", _africa, "middle_east", "southern_europe", "north_africa", "east_africa"),
                    new Region("great_britain", _europe, "iceland", "scandinavia", "northern_europe", "western_europe"),
                    new Region("greenland", _northAmerica, "iceland", "quebec", "ontario", "northwest_territory"),
                    new Region("iceland", _europe, "great_britain", "greenland", "scandinavia"),
                    new Region("india", _asia, "middle_east", "afghanistan", "china", "siam"),
                    new Region("indonesia", _australia, "siam", "new_guinea", "western_australia"),
                    new Region("irkutsk", _europe, "yakutsk", "kamchatka", "mongolia", "siberia"),
                    new Region("japan", _asia, "kamchatka", "mongolia"),
                    new Region("kamchatka", _asia, "alaska", "irkutsk", "yakutsk", "japan", "mongolia"),
                    new Region("madagascar", _africa, "east_africa", "south_africa"),
                    new Region("middle_east", _asia, "india", "afghanistan", "ukraine", "southern_europe", "egypt",
                               "east_africa"),
                    new Region("mongolia", _asia, "irkutsk", "japan", "kamchatka", "siberia", "china"),
                    new Region("new_guinea", _australia, "eastern_australia", "western_australia", "indonesia"),
                    new Region("northern_europe", _europe, "ukraine", "scandinavia", "great_britain", "western_europe",
                               "southern_europe"),
                    new Region("northwest_territory", _northAmerica, "greenland", "alaska", "alberta", "ontario"),
                    new Region("north_africa", _africa, "western_europe", "southern_europe", "egypt", "east_africa",
                               "congo", "brasil"),
                    new Region("ontario", _northAmerica, "greenland", "northwest_territory", "alberta",
                               "western_united_states",
                               "eastern_united_states", "quebec"),
                    new Region("peru", _southAmerica, "venezuela", "brasil", "argentina"),
                    new Region("quebec", _northAmerica, "greenland", "ontario", "eastern_united_states"),
                    new Region("scandinavia", _europe, "iceland", "ukraine", "northern_europe", "great_britain"),
                    new Region("siam", _asia, "china", "india", "indonesia"),
                    new Region("siberia", _asia, "yakutsk", "irkutsk", "mongolia", "china", "ural"),
                    new Region("southern_europe", _europe, "ukraine", "northern_europe", "western_europe",
                               "north_africa", "egypt", "middle_east"),
                    new Region("south_africa", _africa, "congo", "east_africa", "madagascar"),
                    new Region("ukraine", _europe, "scandinavia", "northern_europe", "southern_europe",
                               "middle_east", "afghanistan", "ural"),
                    new Region("ural", _asia, "china", "siberia", "afghanistan", "ukraine"),
                    new Region("venezuela", _southAmerica, "central_america", "peru", "brasil"),
                    new Region("western_australia", _australia, "eastern_australia", "new_guinea", "indonesia"),
                    new Region("western_europe", _europe, "great_britain", "northern_europe", "southern_europe",
                               "north_africa"),
                    new Region("western_united_states", _northAmerica, "alberta", "ontario", "eastern_united_states",
                               "central_america"),
                    new Region("yakutsk", _europe, "kamchatka", "irkutsk", "siberia")
                };
        }
        public void InitializeArmies()
        {
            foreach (var p in Players)
                Armies.Add(new Army(p));
        }
        public void InitializeArmyReserve()
        {
            foreach (var army in Armies)
                army.Reserve = 50 - 5*Players.Count();
        }
        public void AssignRegions()
        {
            int playerIndex = 0;
            int maxPlayerIndex = Players.Count();
            var random = new Random();
            while (UnassignedRegions.Count() != 0)
            {
                //kies de random region en de speler
                var randomRegion = UnassignedRegions[random.Next(UnassignedRegions.Count())];
                var chosenPlayer = Players[playerIndex];

                //verwijder de gekozen region van unassignedRegions
                UnassignedRegions.Remove(randomRegion);

                var chosenPlayerArmy = Armies.Find(army => army.Player == chosenPlayer);

                // 1 troep van reserve naar het gekozen gebied
                chosenPlayerArmy.TroopGroup.Add(new Troop(1, randomRegion));
                chosenPlayerArmy.Reserve -= 1;

                //volgende playerIndex selecteren
                playerIndex = (playerIndex + 1) % maxPlayerIndex;
            }
        }
        public bool EndStage(int playerId)
        {
            var player = Players[playerId];
            var playerArmy = Armies[playerId];
            switch (player.PlayerState)
            {
                case PlayerState.Initial:

                    // troepen moeten geplaatst zijn om over te gaan naar wacht state
                    if (playerArmy.Reserve != 0)
                        return false; // troepen nog niet geplaatst

                    player.PlayerState = PlayerState.Waiting;
                    if (Players.All(p => p.PlayerState == PlayerState.Waiting))
                    {
                        var firstPlayer = Players[0];
                        firstPlayer.PlayerState = PlayerState.Reinforcing;
                        Armies[0].AddReinforcements();
                    }
                    return true;
                case PlayerState.Reinforcing:
                    if (playerArmy.Reserve != 0)
                        return false;
                    player.PlayerState = PlayerState.Attacking;
                    return true;
               case PlayerState.Attacking:
                    player.PlayerState = PlayerState.Moving;
                    break;
               case PlayerState.Moving:
                    player.PlayerState = PlayerState.Waiting;
                    var nextPlayerId = (Players.IndexOf(player) + 1) % Players.Count;
                    Players[nextPlayerId].PlayerState = PlayerState.Reinforcing;
                    Armies[nextPlayerId].AddReinforcements();
                    break;
            }
            return true;
        }

        // true als iedere region dezelfde speler als eigenaar heeft.
        public Boolean GameOver()
        {
            // eerste army met een troep met een grootte verschillend van 0
            var firstNonEmptyArmy = Armies.First(army => army.TroopGroup.Any(troopgroup => troopgroup.TroopSize != 0));
            int nrOfTroops = firstNonEmptyArmy.TroopGroup.Count;
            int nrOfRegions = Continents.SelectMany(c => c.Regions).Count();
            return nrOfTroops == nrOfRegions;
        }
        public Player Winner()
        {
            return  GameOver() ? Armies.First(army => army.TroopGroup.Any(troopgroup => troopgroup.TroopSize != 0)).Player : null;
        }


        public Throw Attack(int playerRiskId, string from, string to, int numberOfDice)
        {
            var playerArmy = Armies[playerRiskId];

            if (!playerArmy.HasRegion(from))
                return null;
            if (playerArmy.HasRegion(to))
                return null;
            var fromTroop = GetTroopGroup(from);
            var toTroop = GetTroopGroup(to);
            if (!fromTroop.CanAttackWith(numberOfDice))
                return null;
            var result = new Throw(numberOfDice, toTroop.Defenders);
            fromTroop.RemoveTroops(result.AttackersDead);
            toTroop.RemoveTroops(result.DefendersDead);

            result.Victory = toTroop.TroopSize == 0;

            if (result.Victory)
            {
                // maak aanvaller nieuwe eigenaar van het veroverde gebied
                Armies.Find(a => a.TroopGroup.Contains(toTroop)).TroopGroup.Remove(toTroop);
                playerArmy.TroopGroup.Add(toTroop);

                //overlevende aanvallers naar veroverde gebied verplaatsen
                fromTroop.MoveTroops(toTroop, numberOfDice - result.AttackersDead);
            }
            return result;
        }
        public Troop GetTroopGroup(string regionId)
        {
            return Armies.SelectMany(army => army.TroopGroup).First(t => t.Region.Id == regionId);
        }

        public bool Move(int playerId, string from, string to, int number)
        {
            var fromTroop = GetTroopGroup(from);
            var toTroop = GetTroopGroup(to);
            var playerArmy = Armies[playerId];
            if (!playerArmy.HasTroop(fromTroop))
                return false;
            if (!playerArmy.HasTroop(toTroop))
                return false;
            if (!fromTroop.CanMove(number))
                return false;
            fromTroop.MoveTroops(toTroop, number);
            return true;
        }
    }
}
